{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "14c9d060",
   "metadata": {},
   "source": [
    "# Python Basics\n",
    "python基础知识与难点，包括：\n",
    "\n",
    "\n",
    "* 数据结构。\n",
    "* 浅拷贝与深拷贝。\n",
    "* 函数定义。\n",
    "* 函数高级特性。\n",
    "* 异常处理\n",
    "* 类与面向对象。\n",
    "\n",
    "<br/>主要参考：\n",
    "<br/>廖雪峰老师python教程：https://www.liaoxuefeng.com/wiki/1016959663602400\n",
    "<br/>Python基础教程：https://www.runoob.com/python/python-tutorial.html\n",
    "<br/>深拷贝与浅拷贝：https://zhuanlan.zhihu.com/p/54011712\n",
    "<br/>魔法函数：https://zhuanlan.zhihu.com/p/24567545"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90af4f6e",
   "metadata": {},
   "source": [
    "## Common Data Structures: List\n",
    "常见数据结构：list，有序集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 146,
   "id": "5de91155",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://www.runoob.com/wp-content/uploads/2014/08/list_slicing1_new1.png\", width=1366, heigth=720>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%html\n",
    "<img src=\"https://www.runoob.com/wp-content/uploads/2014/08/list_slicing1_new1.png\", width=1366, heigth=720>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 243,
   "id": "80604642",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b  [1, [], '2']\n",
      "b[0]  1\n",
      "b[:1]  [1]\n",
      "b[1:]  [[], '2']\n",
      "b[2:3]  ['2']\n",
      "b[-1]  2\n",
      "b[:-2]  [1]\n",
      "b.append(5)  [1, [], '2', 5]\n",
      "b.extend([1,2])  [1, [], '2', 5, 1, 2]\n",
      "b.insert(1,'haha')   [1, 'haha', [], '2', 5, 1, 2]\n",
      "del b[0]  ['haha', [], '2', 5, 1, 2]\n",
      "b.remove(1)\n",
      "['haha', [], '2', 5, 2]\n",
      "b.append(5)\n",
      "['haha', [], '2', 5, 2, 5]\n"
     ]
    }
   ],
   "source": [
    "a = []\n",
    "# a = list()\n",
    "b= [1,a,'2']\n",
    "print('b ',b)\n",
    "print('b[0] ',b[0])\n",
    "print('b[:1] ',b[:1])\n",
    "print('b[1:] ',b[1:])\n",
    "print('b[2:3] ',b[2:3])\n",
    "print('b[-1] ',b[-1])\n",
    "print('b[:-2] ',b[:-2])\n",
    "\n",
    "b.append(5)\n",
    "print('b.append(5) ',b)\n",
    "\n",
    "b.extend([1,2])\n",
    "print('b.extend([1,2]) ',b)\n",
    "\n",
    "b.insert(1,'haha')\n",
    "print('b.insert(1,\\'haha\\')',' ',b)\n",
    "\n",
    "del b[0]\n",
    "print('del b[0] ',b)\n",
    "print('b.remove(1)')\n",
    "b.remove(1)\n",
    "print(b)\n",
    "print('b.append(5)')\n",
    "b.append(5)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "id": "a32534cd",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[[1, 2], [3, 4], [5, 6], [7, 8]]"
      ]
     },
     "execution_count": 148,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#使用列表构建矩阵\n",
    "matrix  = [[1,2],[3,4],[5,6],[7,8]]\n",
    "matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "id": "00d26c8d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1, 3, 5, 7], [2, 4, 6, 8]]\n",
      "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n"
     ]
    }
   ],
   "source": [
    "#循环\n",
    "print([[row[i] for row in matrix] for i in range(2)])\n",
    "print([i*2 for i in range(10)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "84cc48de",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#判断b序列是否包含a序列,元素顺序也必须一致\n",
    "def fun_isin(a,b):\n",
    "    return any([a==b[i:i+len(a)] for i in range(0,len(b)-len(a)+1)])\n",
    "fun_isin(a=[1,2],b=[1,2,3])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff1fb59c",
   "metadata": {},
   "source": [
    "## Common Data Structures: Set\n",
    "常见数据结构：set.可以看成数学意义上的无序和无重复元素的集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "id": "e03d7fc8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1, 3}\n",
      "{1, 2, 3}\n",
      "{1, 2, 3, 4}\n",
      "{1, 2, 3, 4, 5, 6, 8}\n"
     ]
    }
   ],
   "source": [
    "my_set = {1, 3}\n",
    "print(my_set)\n",
    "my_set.add(2)\n",
    "print(my_set)\n",
    "my_set.update([2, 3, 4])\n",
    "print(my_set)\n",
    "my_set.update([4, 5], {1, 6, 8})\n",
    "print(my_set)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 155,
   "id": "893534a3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{1, 2, 3, 4, 5, 6, 8}"
      ]
     },
     "execution_count": 155,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_set.add(1)\n",
    "my_set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 156,
   "id": "5b65b11f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{2, 3, 4, 5, 6, 8}"
      ]
     },
     "execution_count": 156,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_set.remove(1)\n",
    "my_set"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5d568a6",
   "metadata": {},
   "source": [
    "数学上常见的集合操作"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 158,
   "id": "7b6e04a8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1, 2, 3, 4, 5}\n",
      "{1, 2}\n",
      "{4, 5}\n",
      "{1, 2, 3, 4, 5}\n",
      "{3}\n",
      "{1, 2, 4, 5}\n"
     ]
    }
   ],
   "source": [
    "set_a = {1,2,3}\n",
    "set_b = {3,4,5}\n",
    "print(set_a|set_b)\n",
    "print(set_a - set_b)\n",
    "print(set_b - set_a)\n",
    "print(set_a.union(set_b))\n",
    "print(set_a.intersection(set_b))\n",
    "print(set_a^set_b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 181,
   "id": "8a2234d9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[5, 6, 4, 1, 'h', 'a', 'f', 2]\n"
     ]
    }
   ],
   "source": [
    "#set用于排重且排序\n",
    "mylist=[5,6,4,1,'h','a','f','a',2,4,2,1]\n",
    "li =list(set(mylist))\n",
    "li.sort(key=mylist.index)\n",
    "print(li)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cfe2ad49",
   "metadata": {},
   "source": [
    "## Common Data Structures: Dict\n",
    "常见数据结构：字典  \n",
    "\n",
    "Dict is basically a hashmap. Its 'equivalent' in C++ will be unordered map. Therefore it is not with an order. To avoid pain, if you need order, use OrderedDict. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "id": "e46bc6be",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "a = dict()\n",
    "a = {'x':'1', 'y':'2'}\n",
    "print(a['x'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "id": "f19cd890",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': 1, 'b': 2, 'c': 3}\n"
     ]
    }
   ],
   "source": [
    "keys = ['a','b','c']\n",
    "values = [1,2,3]\n",
    "dict_from_zip = dict(zip(keys, values))#将对象中对应的元素打包成一个个tuple（元组），然后返回由这些tuples组成的list（列表）\n",
    "print(dict_from_zip)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "id": "330f2872",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ab'"
      ]
     },
     "execution_count": 168,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def my_concat(**kwargs):#**kwargs表示关键字参数，它是一个dict\n",
    "    result = \"\"\n",
    "    \n",
    "    for k, v in kwargs.items():\n",
    "        result += v\n",
    "    return result\n",
    "my_concat(x=\"a\",y=\"b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 169,
   "id": "8a6ef104",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}\n"
     ]
    }
   ],
   "source": [
    "# You can also use dict comprehension to shorten your code. \n",
    "# 使用字典简化代码\n",
    "odd_squares = {x: x*x for x in range(11) if x % 2 == 1}\n",
    "print(odd_squares)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "563e0317",
   "metadata": {},
   "source": [
    "### 少用数据结构：tuple\n",
    "另一种有序列表叫元组：tuple。tuple和list非常类似，但是tuple一旦初始化就不能修改"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 174,
   "id": "a01a0d0f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Michael\n"
     ]
    },
    {
     "ename": "TypeError",
     "evalue": "'tuple' object does not support item assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-174-874ece666701>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mclassmates\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m'Michael'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Bob'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'Tracy'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mclassmates\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mclassmates\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'a'\u001b[0m\u001b[0;31m#会报错\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: 'tuple' object does not support item assignment"
     ]
    }
   ],
   "source": [
    "classmates = ('Michael', 'Bob', 'Tracy')\n",
    "print(classmates[0])\n",
    "classmates[0]='a'#会报错"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "054f7732",
   "metadata": {},
   "source": [
    "## Common Data Structure: NamedTuple\n",
    "常见数据结构：namedtuple.  \n",
    "是一个函数，它用来创建一个自定义的tuple对象，并且规定了tuple元素的个数，并可以用属性而不是索引来引用tuple的某个元素。  \n",
    "这样一来，我们用namedtuple可以很方便地定义一种数据类型，它具备tuple的不变性，又可以根据属性来引用，使用十分方便。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 170,
   "id": "75b0d400",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Employee(age=10, place='beijing', education='none')\n"
     ]
    }
   ],
   "source": [
    "from collections import namedtuple\n",
    "employee = namedtuple('Employee', ['age','place', 'education'])\n",
    "tom = employee(age=10, place='beijing', education='none')\n",
    "print(tom)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b88b2161",
   "metadata": {},
   "source": [
    "## Common Data Structure: dataclass\n",
    "---\n",
    "一种新的更方便的装饰器  \n",
    "Data class is a great way to pass many parameters to a function. It helps with documentation, with range check, so people won't just stack anything into it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 178,
   "id": "9aa93b21",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dataclasses import dataclass, field\n",
    "from typing import Optional\n",
    "\n",
    "@dataclass\n",
    "class MyDataClass:\n",
    "    name : str = field(\n",
    "    default='tom',\n",
    "    metadata={'help':\"Name of the person\"})\n",
    "    \n",
    "    age: Optional[int] = field(\n",
    "    default = None,\n",
    "    metadata={'help':\"Age of the pesson. Optional.\"})\n",
    "    \n",
    "    vip: int = field(\n",
    "    default = 100,\n",
    "    metadata = {'help':\"Some very important field.\"})\n",
    "        \n",
    "\n",
    "    def __post_init__(self): # This function will help you to handle illegal argument. \n",
    "        if self.vip <= 0:\n",
    "            raise Exception(\"That important thing has to be larger than 0\")\n",
    "            \n",
    "    @property#Property 是 Python 类的一个内置的 装饰器. @property 的作用是 将一个方法, 变为属性来调用.\n",
    "    def age_type(self):\n",
    "        if self.age >= 100:\n",
    "            return 'You are old'\n",
    "        else:\n",
    "            return 'You are still young'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 179,
   "id": "e901de20",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyDataClass(name='jerry', age=20, vip=100)\n"
     ]
    }
   ],
   "source": [
    "my_data_class = MyDataClass(name='jerry', age = 20)\n",
    "print(my_data_class)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 180,
   "id": "3cd42fa0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n",
      "You are still young\n"
     ]
    }
   ],
   "source": [
    "print(my_data_class.age)\n",
    "print(my_data_class.age_type)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c55b7db",
   "metadata": {},
   "source": [
    "## 浅拷贝与深拷贝"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4bafa47",
   "metadata": {},
   "source": [
    "1.赋值: 只是复制了新对象的引用，不会开辟新的内存空间。\n",
    "\n",
    "并不会产生一个独立的对象单独存在，只是将原有的数据块打上一个新标签，所以当其中一个标签被改变的时候，数据块就会发生变化，另一个标签也会随之改变。\n",
    "\n",
    "\n",
    "\n",
    "2.浅拷贝: 创建新对象，其内容是原对象的引用。\n",
    "\n",
    "浅拷贝有三种形式： 切片操作，工厂函数，copy模块中的copy函数。\n",
    "\n",
    "如： lst = [1,2,[3,4]]\n",
    "\n",
    "切片操作：lst1 = lst[:] 或者 lst1 = [each for each in lst]\n",
    "\n",
    "工厂函数：lst1 = list(lst)\n",
    "\n",
    "copy函数：lst1 = copy.copy(lst)\n",
    "\n",
    "浅拷贝之所以称为浅拷贝，是它仅仅只拷贝了一层，拷贝了最外围的对象本身，内部的元素都只是拷贝了一个引用而已，在lst中有一个嵌套的list[3,4]，如果我们修改了它，情况就不一样了。\n",
    "\n",
    "浅复制要分两种情况进行讨论：\n",
    "\n",
    "1）当浅复制的值是不可变对象（字符串、元组、数值类型）时和“赋值”的情况一样，对象的id值（id()函数用于获取对象的内存地址）与浅复制原来的值相同。\n",
    "\n",
    "2）当浅复制的值是可变对象（列表、字典、集合）时会产生一个“不是那么独立的对象”存在。有两种情况：\n",
    "\n",
    "第一种情况：复制的对象中无复杂子对象，原来值的改变并不会影响浅复制的值，同时浅复制的值改变也并不会影响原来的值。原来值的id值与浅复制原来的值不同。\n",
    "\n",
    "第二种情况：复制的对象中有复杂子对象（例如列表中的一个子元素是一个列表），如果不改变其中复杂子对象，浅复制的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅复制的值。\n",
    "\n",
    "\n",
    "\n",
    "3.深拷贝：和浅拷贝对应，深拷贝拷贝了对象的所有元素，包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象，不再与原来的对象有任何关联。\n",
    "\n",
    "所以改变原有被复制对象不会对已经复制出来的新对象产生影响。\n",
    "\n",
    "只有一种形式，copy模块中的deepcopy函数\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "id": "062fb1df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=====赋值=====\n",
      "(1, 2, 3)\n",
      "(1, 2, 3)\n",
      "140638627248000\n",
      "140638627248000\n",
      "=====浅拷贝=====\n",
      "(1, 2, 3)\n",
      "(1, 2, 3)\n",
      "140638627248000\n",
      "140638627248000\n",
      "=====深拷贝=====\n",
      "(1, 2, 3)\n",
      "(1, 2, 3)\n",
      "140638627248000\n",
      "140638627248000\n"
     ]
    }
   ],
   "source": [
    "#对于不可变对象的深浅拷贝\n",
    "#不可变类型，不管是深拷贝还是浅拷贝，地址值和拷贝后的值都是一样的。\n",
    "import copy\n",
    "a=(1,2,3)\n",
    "\n",
    "print(\"=====赋值=====\")\n",
    "b=a\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))\n",
    "\n",
    "print(\"=====浅拷贝=====\")\n",
    "b=copy.copy(a)\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))\n",
    "\n",
    "print(\"=====深拷贝=====\")\n",
    "b=copy.deepcopy(a)\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "df6e6d7a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=====赋值=====\n",
      "[1, 2, 3]\n",
      "[1, 2, 3]\n",
      "140638630283376\n",
      "140638630283376\n",
      "=====浅拷贝=====\n",
      "[1, 2, 3]\n",
      "[1, 2, 3]\n",
      "140638630283376\n",
      "140638630274064\n",
      "=====深拷贝=====\n",
      "[1, 2, 3]\n",
      "[1, 2, 3]\n",
      "140638630283376\n",
      "140638628191440\n"
     ]
    }
   ],
   "source": [
    "#对于可变对象深浅拷贝\n",
    "#赋值： 值相等，地址相等\n",
    "#copy浅拷贝：值相等，地址不相等\n",
    "#deepcopy深拷贝：值相等，地址不相等\n",
    "import copy\n",
    "a=[1,2,3]\n",
    "\n",
    "print(\"=====赋值=====\")\n",
    "b=a\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))\n",
    "\n",
    "print(\"=====浅拷贝=====\")\n",
    "b=copy.copy(a)\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))\n",
    "\n",
    "print(\"=====深拷贝=====\")\n",
    "b=copy.deepcopy(a)\n",
    "print(a)\n",
    "print(b)\n",
    "print(id(a))\n",
    "print(id(b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "id": "5aefc35e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, [4, 5], 6]\n",
      "[1, 2, 3, [4, 5], 6]\n",
      "[1, 2, 3, [4, 5]]\n",
      "[1, 2, 3, [4, 5]]\n"
     ]
    }
   ],
   "source": [
    "#对于可变对象深浅拷贝(外层改变元素)\n",
    "import copy\n",
    "l=[1,2,3,[4, 5]]\n",
    "\n",
    "l1=l #赋值\n",
    "l2=copy.copy(l) #浅拷贝\n",
    "l3=copy.deepcopy(l) #深拷贝\n",
    "l.append(6)\n",
    "\n",
    "print(l)  \n",
    "print(l1)\n",
    "print(l2)\n",
    "print(l3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 162,
   "id": "0676acc6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, [4, 5, 6]]\n",
      "[1, 2, 3, [4, 5, 6]]\n",
      "[1, 2, 3, [4, 5, 6]]\n",
      "[1, 2, 3, [4, 5]]\n"
     ]
    }
   ],
   "source": [
    "#对于可变对象深浅拷贝(内层改变元素)\n",
    "import copy\n",
    "l=[1,2,3,[4, 5]]\n",
    "\n",
    "l1=l #赋值\n",
    "l2=copy.copy(l) #浅拷贝\n",
    "l3=copy.deepcopy(l) #深拷贝\n",
    "l[3].append(6) \n",
    "\n",
    "print(l) \n",
    "print(l1)\n",
    "print(l2)\n",
    "print(l3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d57e42fc",
   "metadata": {},
   "source": [
    "1.  外层添加元素时，浅拷贝不会随原列表变化而变化；内层添加元素时，浅拷贝才会变化。\n",
    "2.  无论原列表如何变化，深拷贝都保持不变。\n",
    "3.  赋值对象随着原列表一起变化。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4eb30b13",
   "metadata": {},
   "source": [
    "## Define Functions函数定义"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07bedb15",
   "metadata": {},
   "source": [
    "`*args` is called list unpacks. Inside the function, the `args` are just like lists.   \n",
    "`*args`, `**kwargs`表示函数的可变参数\n",
    "`*args` 表示任何多个无名参数，它是一个tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "bad8db47",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def mysum(*args):\n",
    "    result = 0\n",
    "    for x in args:\n",
    "        result += x\n",
    "    return result\n",
    "mysum(1,2,3) # Works\n",
    "mysum(2,3,5,6) #Works"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b15a6aeb",
   "metadata": {},
   "source": [
    "On the other hand, `**kwargs` is called keyword argument. It is basically a python dictionary.  \n",
    "`**kwargs`表示关键字参数，它是一个dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3a53091c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ab'"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def my_concat(**kwargs):\n",
    "    result = \"\"\n",
    "    for k, v in kwargs.items():\n",
    "        result += v\n",
    "    return result\n",
    "my_concat(x=\"a\",y=\"b\") # works,变量类型必须一致"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77d504cf",
   "metadata": {},
   "source": [
    "参数`arg`、`*args`、`**kwargs`三个参数的位置必须是一定的。必须是(`arg`,`*args`,`**kwargs`)这个顺序"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "ffd6c1d5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6 (7, 8, 9) {'a': 1, 'b': 2, 'c': 3}\n"
     ]
    }
   ],
   "source": [
    "def my_fun(arg,*args,**kwargs):\n",
    "    print(arg,args,kwargs)\n",
    "\n",
    "my_fun(6,7,8,9,a=1, b=2, c=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0ccd670",
   "metadata": {},
   "source": [
    "参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0f99604",
   "metadata": {},
   "source": [
    "## 函数的高级特性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 209,
   "id": "6fbc31dc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#空函数,pass\n",
    "def nop():\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 216,
   "id": "e6a4d59d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 2, 4, 6, 8]\n",
      "ACEG\n"
     ]
    }
   ],
   "source": [
    "#切片\n",
    "L = list(range(100))\n",
    "##前10个数，每两个取一个：\n",
    "print(L[:10:2])\n",
    "mystr='ABCDEFG'\n",
    "print(mystr[::2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 227,
   "id": "d2ee833a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a\n",
      "b\n",
      "c\n",
      "c\n",
      "c\n",
      "c\n",
      "a 1\n",
      "b 2\n",
      "c 3\n",
      "True\n",
      "True\n",
      "False\n",
      "0 A\n",
      "1 B\n",
      "2 C\n",
      "John - 1983\n",
      "Jim - 1985\n",
      "Lucy - 1995\n",
      "('John', 'Lucy', 'Max')\n",
      "(1995, 2000, 1985)\n",
      "(0, 3, 9)\n"
     ]
    }
   ],
   "source": [
    "#迭代，用于循环的简化\n",
    "##字典迭代\n",
    "d = {'a': 1, 'b': 2, 'c': 3}\n",
    "for key in d:\n",
    "    print(key)\n",
    "    \n",
    "for value in d.values():\n",
    "    print(key)\n",
    "    \n",
    "for k, v in d.items():\n",
    "    print(k,v)\n",
    "\n",
    "##判断是否可以迭代\n",
    "from collections.abc import Iterable\n",
    "print(isinstance('abc', Iterable)) # str是否可迭代\n",
    "print(isinstance([1,2,3], Iterable)) # list是否可迭代\n",
    "print(isinstance(123, Iterable)) # 整数是否可迭代\n",
    "\n",
    "##enumerate同时循环元素与下标\n",
    "for i, value in enumerate(['A', 'B', 'C']):\n",
    "    print(i, value)\n",
    "    \n",
    "##zip函数来可以把2个或多个列表合并，并创建一个元组对的列表。\n",
    "##元组对的数量以合并列表的最短长度为准。\n",
    "name = [\"John\", \"Jim\", \"Lucy\"]\n",
    "year = [1983, 1985, 1995]\n",
    "for name, year in zip(name, year):\n",
    "    print(\"{} - {}\".format(name, year))\n",
    "    \n",
    "##利用zip(*some_list)方法可以实现元组列表的反向解压。\n",
    "##注意只支持元组列表，不支持dict直接解压\n",
    "l1 = [(\"John\", 1995), (\"Lucy\", 2000), (\"Max\", 1985)]\n",
    "name1, year1 = zip(*l1)\n",
    "print(name1)\n",
    "print(year1)\n",
    "\n",
    "##使用 product 扁平化多层嵌套循环,相当于笛卡尔积\n",
    "from itertools import product\n",
    "##找到加和为12的三个数\n",
    "def find_twelve_v2(num_list1, num_list2, num_list3):\n",
    "    for num1, num2, num3 in product(num_list1, num_list2, num_list3):\n",
    "        if num1 + num2 + num3 == 12:\n",
    "            return num1, num2, num3\n",
    "print(find_twelve_v2(num_list1=range(0,10), num_list2=range(0,10), num_list3=range(0,10)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 230,
   "id": "408d4e0b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]\n",
      "['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']\n",
      "[-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]\n"
     ]
    }
   ],
   "source": [
    "#列表生成式\n",
    "print([x * x for x in range(1, 11)])\n",
    "print([m + n for m in 'ABC' for n in 'XYZ'])\n",
    "##有条件\n",
    "print([x if x % 2 == 0 else -x for x in range(1, 11)])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 233,
   "id": "8009b7dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "4\n",
      "9\n",
      "16\n",
      "25\n",
      "36\n",
      "49\n",
      "64\n",
      "81\n",
      "<generator object fib at 0x7fe8fbd5bc50>\n",
      "g: 1\n",
      "g: 1\n",
      "g: 2\n",
      "g: 3\n",
      "g: 5\n",
      "g: 8\n",
      "Generator return value: done\n"
     ]
    }
   ],
   "source": [
    "#生成器\n",
    "#在循环的过程中不断推算出后续的元素，不必创建完整的list，节省大量的空间\n",
    "##方法1：圆括号\n",
    "g = (x * x for x in range(10))\n",
    "for n in g:\n",
    "    print(n)\n",
    "##方法2：yield\n",
    "def fib(max):\n",
    "    n, a, b = 0, 0, 1\n",
    "    while n < max:\n",
    "        yield b\n",
    "        a, b = b, a + b\n",
    "        n = n + 1\n",
    "    return 'done'\n",
    "print(fib(6))\n",
    "\n",
    "##结合异常捕获\n",
    "g = fib(6)\n",
    "while True:\n",
    "    try:\n",
    "        x = next(g)\n",
    "        print('g:', x)\n",
    "    except StopIteration as e:\n",
    "        print('Generator return value:', e.value)\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "49cd4874",
   "metadata": {},
   "source": [
    "高手设计的尾递归优化，\n",
    "用了抛出异常然后自己捕获的方式来打破调用栈的增长  \n",
    "https://zhuanlan.zhihu.com/p/72564126\n",
    "\n",
    "1.  fib(1000, 0, 1)在执行前首先进入装饰器函数，f.f_back为main()函数也就是调用fib的函数\n",
    "2.  fib(1000, 0, 1)下的f.f_back.f_back为None，所以进入else逻辑（调用栈深度为1）\n",
    "3.  执行fib(1000, 0, 1)\n",
    "4.  fib(1000, 0, 1)调用fib(999, 1, 1)\n",
    "5.  这时函数fib(999, 1, 1)进入装饰器函数，符合if条件（调用栈深度为2），执行raise TailRecurseException,并将(999, 1, 1)参数传入TailRecurseException\n",
    "6.  该参数被fib(1000, 0, 1)的except捕捉，通过TailRecurseException将参数(999, 1, 1)传递给fib()，从而进行下一次调用\n",
    "7.  重复过程4 - 6"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 214,
   "id": "a7c42309",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875\n"
     ]
    }
   ],
   "source": [
    "import sys as sys\n",
    "#设计装饰器函数\n",
    "class TailRecurseException(BaseException):\n",
    "    def __init__(self, args, kwargs):\n",
    "        self.args = args\n",
    "        self.kwargs = kwargs\n",
    "\n",
    "\n",
    "def tail_call_optimized(g):\n",
    "    \"\"\"\n",
    "    This function decorates a function with tail call\n",
    "    optimization. It does this by throwing an exception\n",
    "    if it is it's own grandparent, and catching such\n",
    "    exceptions to fake the tail call optimization.\n",
    "\n",
    "    This function fails if the decorated5\n",
    "    function recurses in a non-tail context.\n",
    "    \"\"\"\n",
    "    def func(*args, **kwargs):\n",
    "        f = sys._getframe()\n",
    "        if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:\n",
    "            raise TailRecurseException(args, kwargs)\n",
    "        else:\n",
    "            while 1:\n",
    "                try:\n",
    "                    return g(*args, **kwargs)\n",
    "                except TailRecurseException as e:\n",
    "                    args = e.args\n",
    "                    kwargs = e.kwargs\n",
    "\n",
    "    func.__doc__ = g.__doc__\n",
    "    return func\n",
    "#调用\n",
    "@tail_call_optimized\n",
    "def fib(i, current = 0, next = 1):\n",
    "    if i == 0:\n",
    "        return current\n",
    "    else:\n",
    "        return fib(i - 1, next, current + next)\n",
    "\n",
    "print(fib(1000))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4404094",
   "metadata": {},
   "source": [
    "## Basic Functional Programming in Python  \n",
    "函数式编程基础"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6cd8ae26",
   "metadata": {},
   "source": [
    "### Common Higher Order Function  \n",
    "既然变量可以指向函数，而函数的参数又能接收变量，  \n",
    "那么一个函数就可以接收另外一个函数作为参数。  \n",
    "这样的函数就是高阶函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 194,
   "id": "d1ecc0cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<map object at 0x7fe8fb752ed0>\n",
      "[2, 3, 4, 5, 6, 7, 7]\n"
     ]
    }
   ],
   "source": [
    "my_input = [1,2,3,4,5,6,6]\n",
    "result = map(lambda x: x+1, my_input)\n",
    "print(result) # map is lazy\n",
    "print(list(result))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 196,
   "id": "12284eae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "29\n"
     ]
    }
   ],
   "source": [
    "from functools import reduce\n",
    "result = reduce(lambda x, y: x+y, filter(lambda x: x > 3, map(lambda x: x+1, my_input)))\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fbecbee",
   "metadata": {},
   "source": [
    "### Decorators  \n",
    "在代码运行期间动态增加功能的方式，称之为“装饰器”（Decorator）。  \n",
    "本质上，decorator就是一个返回函数的高阶函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 202,
   "id": "4e36f9db",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "def my_decorator(func):\n",
    "    def my_decorator_impl(x):\n",
    "        result = x if x > 0 else 0\n",
    "        return func(result)\n",
    "    return my_decorator_impl\n",
    "\n",
    "@my_decorator\n",
    "def myfunc(x):\n",
    "    return np.sqrt(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 203,
   "id": "82253ddc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This is the same\n",
    "new_func = my_decorator(myfunc)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 204,
   "id": "48bd0907",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 204,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "myfunc(-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 207,
   "id": "07387ae5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.4142135623730951"
      ]
     },
     "execution_count": 207,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#当函数的参数个数太多，需要简化时，使用functools.partial可以创建一个新的函数，\n",
    "#这个新函数可以固定住原函数的部分参数，从而在调用时更简单。\n",
    "from functools import partial\n",
    "def decor_impl(fun, argument):\n",
    "    def impl(x):\n",
    "        result = x if x > argument else argument\n",
    "        return fun(result)\n",
    "    return impl\n",
    "\n",
    "decor = partial(decor_impl, argument = 2)\n",
    "\n",
    "@decor\n",
    "def myfunc(x):\n",
    "    return np.sqrt(x)\n",
    "myfunc(-1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 237,
   "id": "c4db02a9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['1', '2', '3', '4', '5', '6', '7', '8', '9']\n",
      "13579\n"
     ]
    }
   ],
   "source": [
    "#map:将传入的函数依次作用到序列的每个元素，并把结果作为新的Iterator返回。\n",
    "print(list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])))\n",
    "\n",
    "#reduce:把一个函数作用在一个序列上，这个函数必须接收两个参数，\n",
    "#reduce把结果继续和序列的下一个元素做累积计算\n",
    "#reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)\n",
    "from functools import reduce\n",
    "def fn(x, y):\n",
    "    return x * 10 + y\n",
    "print(reduce(fn, [1, 3, 5, 7, 9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 238,
   "id": "7069dc33",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['A', 'B', 'C']\n"
     ]
    }
   ],
   "source": [
    "#filter\n",
    "#把传入的函数依次作用于每个元素，然后根据返回值是True还是False决定保留还是丢弃该元素。\n",
    "##删除空字符串\n",
    "def not_empty(s):\n",
    "    return s and s.strip()\n",
    "\n",
    "print(list(filter(not_empty, ['A', '', 'B', None, 'C', '  '])))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 245,
   "id": "9d67d6b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-21, -12, 5, 9, 36]\n",
      "[5, 9, -12, -21, 36]\n",
      "['Credit', 'Zoo', 'about', 'bob']\n",
      "['about', 'bob', 'Credit', 'Zoo']\n",
      "['Zoo', 'Credit', 'bob', 'about']\n"
     ]
    }
   ],
   "source": [
    "#排序\n",
    "print(sorted([36, 5, -12, 9, -21]))\n",
    "#自定义\n",
    "print(sorted([36, 5, -12, 9, -21], key=abs))\n",
    "\n",
    "#对字符串排序，是按照ASCII的大小比较的，\n",
    "#由于'Z' < 'a'，结果大写字母Z会排在小写字母a的前面。\n",
    "print(sorted(['bob', 'about', 'Zoo', 'Credit']))\n",
    "\n",
    "#改进\n",
    "print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower))\n",
    "\n",
    "#反向排序\n",
    "print(sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 240,
   "id": "30d6d597",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "直接调用: <function lazy_sum.<locals>.sum at 0x7fe8fbda4b00>\n",
      "真正使用: 25\n"
     ]
    }
   ],
   "source": [
    "#函数作为返回值\n",
    "def lazy_sum(*args):\n",
    "    def sum():\n",
    "        ax = 0\n",
    "        for n in args:\n",
    "            ax = ax + n\n",
    "        return ax\n",
    "    return sum\n",
    "f = lazy_sum(1, 3, 5, 7, 9)\n",
    "print('直接调用:',f)\n",
    "print('真正使用:',f())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 247,
   "id": "a333add1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(93, 100)\n",
      "(93, 100)\n",
      "0    6\n",
      "1    7\n",
      "2    8\n",
      "3    9\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "#匿名函数：lambda表达式\n",
    "##常见用法：作为筛选参数\n",
    "scores = [(93, 100), (92, 99), (95, 94)]\n",
    "print(max(scores, key=lambda x: x[0] + x[1]))\n",
    "print(max(scores, key=sum))\n",
    "##常见用法：创建数据\n",
    "import pandas as pd\n",
    "data = pd.Series([1, 2, 3, 4])\n",
    "print(data.map(lambda x: x + 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 250,
   "id": "1b58523a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "138\n",
      "{'name': 'linux', 'age': 18}\n",
      "[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]\n",
      "{'name': 'linux', 'age': 18}\n",
      "{'name': 'linux', 'age': 20}\n",
      "{'name': 'linux', 'age': 25}\n",
      "[[1, 2], [3, 4], [5, 6], [7, 8], [9, 0]]\n"
     ]
    }
   ],
   "source": [
    "#黑魔法函数:eval，类型转换与动态控制语句\n",
    "\n",
    "print(eval('66+72')) # 把字符串解析成66+72的结果\n",
    "\n",
    "print( eval(\"{'name':'linux','age':18}\") ) # 把字符串转为字典\n",
    "\n",
    "print( eval(\"[[1,2], [3,4], [5,6], [7,8], [9,0]]\") ) # 把字符串转为列表\n",
    "\n",
    "print(eval(\"{'name':'linux','age':age}\",{\"age\":18})) # 传递globals参数值为{“age”:18}\n",
    "\n",
    "print(eval(\"{'name':'linux','age':age}\",{\"age\":20},locals())) # 传递locals参数\n",
    "\n",
    "#更安全的方式\n",
    "#literal_eval() 函数：会判断需要计算的内容计算后是不是合法的python类型，\n",
    "#如果是则进行运算，否则就不进行运算。\n",
    "import ast\n",
    "print( ast.literal_eval(\"{'name':'linux','age':25}\") )\n",
    "print( ast.literal_eval(\"[[1,2], [3,4], [5,6], [7,8], [9,0]]\") )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5804f617",
   "metadata": {},
   "source": [
    "## Exception Handling异常处理\n",
    "\n",
    "---\n",
    "The most commonly used ways for exception handling is to raise an exception (also called throw).   \n",
    "常见方法就是抛出异常"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "dda5cacc",
   "metadata": {},
   "outputs": [],
   "source": [
    "#例子1\n",
    "def raise_exception(x):\n",
    "    raise Exception(\"I am an EXCEPTION!!!\") # Something bad has happend\n",
    "\n",
    "def catcher(x):\n",
    "    try:\n",
    "        # This will run the code. If everything is fine, it will return normally.\n",
    "        # 正常运行\n",
    "        raise_exception(x) \n",
    "    except (TypeError, NameError):\n",
    "        # If a specific error occurs, this will follow the behavior.\n",
    "        # 发生命中某个条件的错误\n",
    "        print(\"I am ok with this!\")\n",
    "    except Exception as e: \n",
    "        # This is often used to handle unknown exception, by letting someone else do the job\n",
    "        # 发生未知错误\n",
    "        raise e\n",
    "    finally: \n",
    "        # This will always execute no matter what\n",
    "        # 所有情况都会运行，可以没有finally语句\n",
    "        print(\"Let us swallow everything when exception occurs!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "5a00d399",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Let us swallow everything when exception occurs!\n"
     ]
    },
    {
     "ename": "Exception",
     "evalue": "I am an EXCEPTION!!!",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mException\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-187b7f8ca604>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mcatcher\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-7-9fccf6753854>\u001b[0m in \u001b[0;36mcatcher\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m      8\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"I am ok with this!\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# This is often used to handle unknown exception, by letting someone else do the job\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     11\u001b[0m     \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;31m# This will always execute no matter what\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Let us swallow everything when exception occurs!\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-7-9fccf6753854>\u001b[0m in \u001b[0;36mcatcher\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mcatcher\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m         \u001b[0mraise_exception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# This will run the code. If everything is fine, it will return normally.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      7\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mTypeError\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mNameError\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# If a specific error occurs, this will follow the behavior.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"I am ok with this!\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-7-9fccf6753854>\u001b[0m in \u001b[0;36mraise_exception\u001b[0;34m(x)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mraise_exception\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m     \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"I am an EXCEPTION!!!\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# Something bad has happend\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mcatcher\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mException\u001b[0m: I am an EXCEPTION!!!"
     ]
    }
   ],
   "source": [
    "catcher(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "6d44505f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "try...\n",
      "except: division by zero\n",
      "finally...\n",
      "END\n"
     ]
    }
   ],
   "source": [
    "#例子2\n",
    "try:\n",
    "    print('try...')\n",
    "    r = 10 / 0\n",
    "    print('result:', r)\n",
    "except ZeroDivisionError as e:\n",
    "    print('except:', e)\n",
    "finally:\n",
    "    print('finally...')\n",
    "print('END')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c4700d1",
   "metadata": {},
   "source": [
    "更好的方式是使用日志，方便记录，报错之后可运行后面的代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "6e86f077",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:root:This is some warning!\n",
      "ERROR:root:Something went wrong!\n"
     ]
    }
   ],
   "source": [
    "#例子1\n",
    "import logging\n",
    "logging.info(\"This is some useful information.\")\n",
    "logging.warning(\"This is some warning!\")\n",
    "logging.error(\"Something went wrong!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "b816e1ec",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR:root:division by zero\n",
      "Traceback (most recent call last):\n",
      "  File \"<ipython-input-12-8c9f10daaf4c>\", line 10, in main\n",
      "    bar('0')\n",
      "  File \"<ipython-input-12-8c9f10daaf4c>\", line 6, in bar\n",
      "    return foo(s) * 2\n",
      "  File \"<ipython-input-12-8c9f10daaf4c>\", line 3, in foo\n",
      "    return 10 / int(s)\n",
      "ZeroDivisionError: division by zero\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "END\n"
     ]
    }
   ],
   "source": [
    "#例子2\n",
    "import logging\n",
    "def foo(s):\n",
    "    return 10 / int(s)\n",
    "\n",
    "def bar(s):\n",
    "    return foo(s) * 2\n",
    "\n",
    "def main():\n",
    "    try:\n",
    "        bar('0')\n",
    "    except Exception as e:\n",
    "        logging.exception(e)\n",
    "\n",
    "main()\n",
    "print('END')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25a159f4",
   "metadata": {},
   "source": [
    "A final option, that is very popular is to use a Monad. Monad is quite complex to explain. So let us see an example.  \n",
    "终极方法：Monad  \n",
    "简单理解：函数传递  \n",
    "参考：  \n",
    "https://mp.weixin.qq.com/s?src=11&timestamp=1630310674&ver=3283&signature=zwBzA4et4wQq533IhZjAMQVbh1XVd*cg9T1IAPAbanoZYv4QS3euLhJB4cSJhKNGifz1ZeVJH8*-XS4t3bOx3QePeb5pu-CDkwk98uzEig1YFaiCECfUn0S2jJ7pmwaf&new=1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "500add6a",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Failure():\n",
    "    def __init__(self, value, failed=False):\n",
    "        self.value = value\n",
    "        self.failed = failed\n",
    "    def get(self):\n",
    "        return self.value\n",
    "    def is_failed(self):\n",
    "        return self.failed\n",
    "    def __str__(self):\n",
    "        return ' '.join([str(self.value), str(self.failed)])\n",
    "    def __or__(self, f):\n",
    "        if self.failed:\n",
    "            return self\n",
    "        try:\n",
    "            x = f(self.get())\n",
    "            return Failure(x)\n",
    "        except:\n",
    "            return Failure(None, True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "32235a62",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-1 False\n"
     ]
    }
   ],
   "source": [
    "# This will work.\n",
    "from operator import neg\n",
    "x = '1'\n",
    "y = Failure(x) | int | neg | str\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "9393cd71",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None True\n"
     ]
    }
   ],
   "source": [
    "# This will not\n",
    "from operator import neg\n",
    "x = 'hahaha'\n",
    "y = Failure(x) | int | neg | str\n",
    "print(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2a1e15b",
   "metadata": {},
   "source": [
    "A beautiful collection of functional programming primitives can be found [here](https://github.com/jasondelaat/pymonad.git). Use the following command to install.  \n",
    "pymonad包是一个更上层的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38254409",
   "metadata": {},
   "source": [
    "## Python Class类与面向对象"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8dcbf17a",
   "metadata": {},
   "source": [
    "### 类和实例"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95c29d9f",
   "metadata": {},
   "source": [
    "面向对象最重要的概念就是类（Class）和实例（Instance），必须牢记类是抽象的模板，比如Student类，而实例是根据类创建出来的一个个具体的“对象”，每个对象都拥有相同的方法，但各自的数据可能不同。  \n",
    "在Python中，定义类是通过`class`关键字：\n",
    "`class`后面紧接着是类名，即`Student`，类名通常是大写开头的单词，紧接着是(object)，表示该类是从哪个类继承下来的，通常，如果没有合适的继承类，就使用object类，这是所有类最终都会继承的类。  \n",
    "定义好了`Student`类，就可以根据`Student`类创建出`Student`的实例，创建实例是通过类名+()实现的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "bb8387c0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Student object at 0x7fe8fb776490>\n",
      "<class '__main__.Student'>\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "    pass\n",
    "bart = Student()\n",
    "print(bart)\n",
    "print(Student)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "fb0c9fa8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#自由地给一个实例变量绑定属性\n",
    "bart.name = 'Bart Simpson'\n",
    "bart.name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "5ec4c3cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "#绑定属性\n",
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ce712a6",
   "metadata": {},
   "source": [
    "特殊方法`__init__`前后分别有两个下划线, 两个下划线开头的函数是声明该属性为私有，不能在类的外部被使用或访问。  \n",
    "第一个参数永远是self，表示创建的实例本身，因此，在`__init__`方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。  \n",
    "有了`__init__`方法，在创建实例的时候，就不能传入空的参数了，必须传入与`__init__`方法匹配的参数，但self不需要传，Python解释器自己会把实例变量传进去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "129460af",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart Simpson\n",
      "59\n"
     ]
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "print(bart.name)\n",
    "print(bart.score)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19065df1",
   "metadata": {},
   "source": [
    "数据封装：   \n",
    "Student实例本身就拥有这些数据，要访问这些数据，就没有必要从外面的函数去访问，可以直接在Student类的内部定义访问数据的函数，这样，就把“数据”给封装起来了。\n",
    "这些封装数据的函数是和Student类本身是关联起来的，我们称之为类的方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "0f615119",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart Simpson: 59\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.name, self.score))\n",
    "bart = Student('Bart Simpson', 59)\n",
    "bart.print_score()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30d81c47",
   "metadata": {},
   "source": [
    "封装的另一个好处是可以给Student类增加新的方法，比如get_grade。  \n",
    "get_grade方法可以直接在实例变量上调用，不需要知道内部实现细节："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "6dcd3f09",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Lisa A\n",
      "Bart C\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, score):\n",
    "        self.name = name\n",
    "        self.score = score\n",
    "\n",
    "    def get_grade(self):\n",
    "        if self.score >= 90:\n",
    "            return 'A'\n",
    "        elif self.score >= 60:\n",
    "            return 'B'\n",
    "        else:\n",
    "            return 'C'\n",
    "lisa = Student('Lisa', 99)\n",
    "bart = Student('Bart', 59)\n",
    "print(lisa.name, lisa.get_grade())\n",
    "print(bart.name, bart.get_grade())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a983df67",
   "metadata": {},
   "source": [
    "和静态语言不同，Python允许对实例变量绑定任何数据，也就是说，对于两个实例变量，虽然它们都是同一个类的不同实例，但拥有的变量名称都可能不同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "98b22ec1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "lisa = Student('Lisa Simpson', 87)\n",
    "bart.age = 8\n",
    "bart.age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "fd33aa3b",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute 'age'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-31-e4c93370f77f>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mlisa\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mage\u001b[0m\u001b[0;31m#会报错，因为Student类没有age属性\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student' object has no attribute 'age'"
     ]
    }
   ],
   "source": [
    "lisa.age#会报错，因为Student类没有age属性"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "430f0a0e",
   "metadata": {},
   "source": [
    "访问限制：  \n",
    "如果要让内部属性不被外部访问，可以把属性的名称前加上两个下划线__，在Python中，实例的变量名如果以__开头，就变成了一个私有变量（private），只有内部可以访问，外部不能访问。  \n",
    "这样就确保了外部代码不能随意修改对象内部的状态，这样通过访问限制的保护，代码更加健壮。  \n",
    "这样做的好处是，在方法中，可以对参数做检查，避免传入无效的参数。  \n",
    "如果外部代码要获取name和score怎么办？可以给Student类增加get_name和get_score这样的方法。  \n",
    "如果又要允许外部代码修改score怎么办？可以再给Student类增加set_score方法。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "1cf35a21",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, score):\n",
    "        self.__name = name\n",
    "        self.__score = score\n",
    "    def print_score(self):\n",
    "        print('%s: %s' % (self.__name, self.__score))\n",
    "    ##获取\n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "    def get_score(self):\n",
    "        return self.__score\n",
    "    ##修改\n",
    "    def set_score(self, score):\n",
    "        if 0 <= score <= 100:\n",
    "            self.__score = score\n",
    "        else:\n",
    "            raise ValueError('bad score')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "90e0223f",
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '__name'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-42-28e1b28f9a50>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mbart\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStudent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'Bart Simpson'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m59\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mbart\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name\u001b[0m\u001b[0;31m#报错\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student' object has no attribute '__name'"
     ]
    }
   ],
   "source": [
    "bart = Student('Bart Simpson', 59)\n",
    "bart.__name#报错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "b6eaa90e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Bart Simpson\n",
      "90\n"
     ]
    }
   ],
   "source": [
    "#获取\n",
    "print(bart.get_name())\n",
    "#修改\n",
    "bart.set_score(90)#如果是-1或101则报错\n",
    "print(bart.get_score())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7656e616",
   "metadata": {},
   "source": [
    "不建议的用法：\n",
    "双下划线开头的实例变量是不是一定不能从外部访问呢？其实也不是。\n",
    "不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name，\n",
    "所以，仍然可以通过_Student__name来访问__name变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 182,
   "id": "3f921af0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bart Simpson'"
      ]
     },
     "execution_count": 182,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bart._Student__name"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f923af63",
   "metadata": {},
   "source": [
    "一个错误例子：  \n",
    "表面上看，外部代码“成功”地设置了__name变量，\n",
    "但实际上这个__name变量和class内部的__name变量不是一个变量！  \n",
    "内部的__name变量已经被Python解释器自动改成了_Student__name，\n",
    "而外部代码给bart新增了一个__name变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "892c455b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "New Name\n",
      "Bart Simpson\n"
     ]
    }
   ],
   "source": [
    "bart.__name = 'New Name' # 设置__name变量！\n",
    "print(bart.__name)\n",
    "print(bart.get_name())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4476a719",
   "metadata": {},
   "source": [
    "继承和多态：  \n",
    "对于一个变量，我们只需要知道它是Animal类型，无需确切地知道它的子类型，就可以放心地调用run()方法，而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上，由运行时该对象的确切类型决定，这就是多态真正的威力：调用方只管调用，不管细节，而当我们新增一种Animal的子类时，只要确保run()方法编写正确，不用管原来的代码是如何调用的。  \n",
    "这就是著名的“开闭”原则：   \n",
    "对扩展开放：允许新增Animal子类；  \n",
    "对修改封闭：不需要修改依赖Animal类型的run_twice()等函数。   \n",
    "继承还可以一级一级地继承下来，就好比从爷爷到爸爸、再到儿子这样的关系。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "ff89dc58",
   "metadata": {},
   "outputs": [],
   "source": [
    "#父类\n",
    "class Animal(object):\n",
    "    def run(self):\n",
    "        print('Animal is running...')\n",
    "#子类\n",
    "class Dog(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Dog is running...')\n",
    "\n",
    "class Cat(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Cat is running...')\n",
    "#新方法\n",
    "def run_twice(animal):\n",
    "    animal.run()\n",
    "    animal.run()\n",
    "    \n",
    "class Tortoise(Animal):\n",
    "    def run(self):\n",
    "        print('Tortoise is running slowly...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "id": "ed1ab086",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running...\n",
      "Animal is running...\n",
      "None\n",
      "Dog is running...\n",
      "Dog is running...\n",
      "None\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "#继承\n",
    "print(run_twice(Animal()))\n",
    "print(run_twice(Dog()))\n",
    "a = list() # a是list类型\n",
    "b = Animal() # b是Animal类型\n",
    "c = Dog() # c是Dog类型\n",
    "print(isinstance(a, list))#判断类型信息\n",
    "print(isinstance(c, Animal))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "id": "373a77d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tortoise is running slowly...\n",
      "Tortoise is running slowly...\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "#多态：传入的任意类型，只要是Animal类或者子类，就会自动调用实际类型的run()方法\n",
    "print(run_twice(Tortoise()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8a82e98",
   "metadata": {},
   "source": [
    "获取对象信息：  \n",
    "基本类型都可以用type()。  \n",
    "判断一个对象是否是函数，types模块中定义的常量。  \n",
    "判断class的类型，isinstance()函数。  \n",
    "获得一个对象的所有属性和方法，dir()函数，它返回一个包含字符串的list。  \n",
    "vars() 函数返回对象object的属性和属性值的字典对象。  \n",
    "locals() 函数会以字典类型返回当前位置的全部局部变量。\n",
    "globals() 函数会以字典类型返回当前位置的全部全局变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "4feffb3e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'int'>\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "print(type(123))\n",
    "print(type(123)==type(456))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "d605033d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['AsyncGeneratorType',\n",
       " 'BuiltinFunctionType',\n",
       " 'BuiltinMethodType',\n",
       " 'ClassMethodDescriptorType',\n",
       " 'CodeType',\n",
       " 'CoroutineType',\n",
       " 'DynamicClassAttribute',\n",
       " 'FrameType',\n",
       " 'FunctionType',\n",
       " 'GeneratorType',\n",
       " 'GetSetDescriptorType',\n",
       " 'LambdaType',\n",
       " 'MappingProxyType',\n",
       " 'MemberDescriptorType',\n",
       " 'MethodDescriptorType',\n",
       " 'MethodType',\n",
       " 'MethodWrapperType',\n",
       " 'ModuleType',\n",
       " 'SimpleNamespace',\n",
       " 'TracebackType',\n",
       " 'WrapperDescriptorType',\n",
       " '_GeneratorWrapper',\n",
       " '__all__',\n",
       " '__builtins__',\n",
       " '__cached__',\n",
       " '__doc__',\n",
       " '__file__',\n",
       " '__loader__',\n",
       " '__name__',\n",
       " '__package__',\n",
       " '__spec__',\n",
       " '_calculate_meta',\n",
       " 'coroutine',\n",
       " 'new_class',\n",
       " 'prepare_class',\n",
       " 'resolve_bases']"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import types\n",
    "def fn():\n",
    "    pass\n",
    "print(type(fn)==types.FunctionType)\n",
    "print(type(abs)==types.BuiltinFunctionType)\n",
    "dir(types)#查看有多少种类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "e00f0529",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "mappingproxy({'__module__': '__main__',\n",
       "              'run': <function __main__.Tortoise.run(self)>,\n",
       "              '__doc__': None})"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "vars(Tortoise)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "3cf02172",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'name': 'xiaodeng', 'version': 2.7, 'time': '2015.11.07'}\n"
     ]
    }
   ],
   "source": [
    "def mytest():\n",
    "    name='xiaodeng'\n",
    "    version=2.7\n",
    "    time='2015.11.07'\n",
    "    print(locals())#查找局部变量，返回一个名字/值对的字典对象。只具备查找权限\n",
    "mytest()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 251,
   "id": "2f7807f7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal\t Cat\t Dog\t Failure\t GhostBus\t Iterable\t L\t MyClass\t MyDataClass\t \n",
      "Optional\t Student\t TailRecurseException\t Tortoise\t Vector\t a\t ast\t b\t bar\t \n",
      "bart\t c\t catcher\t classmates\t copy\t d\t data\t dataclass\t decor\t \n",
      "decor_impl\t deepcopy\t dict_from_zip\t employee\t f\t fib\t field\t find_twelve_v2\t fn\t \n",
      "foo\t g\t ghost_bus\t hypot\t i\t isa\t k\t key\t keys\t \n",
      "l\t l1\t l2\t l3\t lazy_sum\t li\t lisa\t logging\t main\t \n",
      "matrix\t my_class_a\t my_class_b\t my_class_c\t my_concat\t my_data_class\t my_decorator\t my_fun\t my_input\t \n",
      "my_set\t myfunc\t mylist\t mystr\t mysum\t mytest\t n\t name\t name1\t \n",
      "namedtuple\t neg\t new_func\t nop\t not_empty\t np\t odd_squares\t partial\t pd\t \n",
      "product\t raise_exception\t reduce\t result\t run_twice\t scores\t set_a\t set_b\t sys\t \n",
      "tail_call_optimized\t tom\t types\t v\t v1\t v2\t v3\t value\t values\t \n",
      "x\t y\t year\t year1\t \n"
     ]
    }
   ],
   "source": [
    "#who:IPython的魔法命令，查看当前有多少变量\n",
    "%who"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 252,
   "id": "bfa27605",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "144\n"
     ]
    }
   ],
   "source": [
    "#查看变量的内存占用,字节\n",
    "import sys\n",
    "print(sys.getsizeof(mytest))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13de2dcf",
   "metadata": {},
   "source": [
    "神奇的错误：由于passengers使用了[]，会一直像闹鬼一样复制"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "id": "07282eac",
   "metadata": {},
   "outputs": [],
   "source": [
    "class GhostBus:\n",
    "    def __init__(self, passengers=[]):\n",
    "        self.passengers = passengers\n",
    "    \n",
    "    def pick(self, name):\n",
    "        self.passengers.append(name)\n",
    "        \n",
    "    def drop(self, name):\n",
    "        self.passengers.remove(name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "a9549668",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['A Ghost', 'A Ghost', 'A Ghost', 'A Ghost']"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Run this several times\n",
    "ghost_bus = GhostBus()\n",
    "ghost_bus.pick('A Ghost')\n",
    "ghost_bus.passengers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15869a3c",
   "metadata": {},
   "source": [
    "正确写法：初始值为None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "8402fa24",
   "metadata": {},
   "outputs": [],
   "source": [
    "class GhostBus:\n",
    "    def __init__(self, passengers=None):\n",
    "        if passengers is None:\n",
    "            self.passengers = list()\n",
    "        else:\n",
    "            self.passengers = passengers\n",
    "    def pick(self, name):\n",
    "        self.passengers.append(name)\n",
    "    def drop(self, name):\n",
    "        self.passengers.remove(name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "3f4d38f6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['A Ghost']"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Run this several times\n",
    "ghost_bus = GhostBus()\n",
    "ghost_bus.pick('A Ghost')\n",
    "ghost_bus.passengers"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "38e03906",
   "metadata": {},
   "source": [
    "## Magic Functions in Python Object  \n",
    "魔法函数:允许你在类中自定义函数（函数名格式一般为__xx__），并绑定到类的特殊方法中。  \n",
    "比如在类A中自定义__str__()函数，则在调用str(A())时，会自动调用__str__()函数，并返回相应的结果。  \n",
    "在我们平时的使用中，可能经常使用__init__函数和__del__函数，其实这也是魔法函数的一种。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 187,
   "id": "e491a300",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Vector:\n",
    "    def __init__(self, x=0, y=0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return 'Vector(%r,%r)' % (self.x, self.y)\n",
    "    def __str__(self):                              \n",
    "        return 'Vector(%r,%r)' % (self.x, self.y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 188,
   "id": "fb104431",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector(1,2)\n",
      "Vector(1,2)\n"
     ]
    }
   ],
   "source": [
    "v = Vector(1,2)\n",
    "print(str(v))\n",
    "print(v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 189,
   "id": "49a91437",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Vector:\n",
    "    def __init__(self, x=0, y=0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return 'Vector(%r,%r)' % (self.x, self.y)\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        x = self.x + other.x\n",
    "        y = self.y + other.y\n",
    "        return Vector(x, y)\n",
    "    \n",
    "    def __sub__(self, other):\n",
    "        x = self.x - other.x\n",
    "        y = self.y - other.y\n",
    "        return Vector(x, y)\n",
    "    \n",
    "    def __mul__(self, scalar):\n",
    "        return Vector(self.x * scalar, self.y * scalar)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 190,
   "id": "b541eaa9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Vector(0,0)"
      ]
     },
     "execution_count": 190,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "v1 = Vector(0,0)\n",
    "v2 = Vector(1,2)\n",
    "\n",
    "v1 *= 2\n",
    "v1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 191,
   "id": "4fc11802",
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import hypot\n",
    "\n",
    "class Vector:\n",
    "    def __init__(self, x=0, y=0):\n",
    "        self.x = x\n",
    "        self.y = y\n",
    "\n",
    "    def __repr__(self):\n",
    "        return 'Vector(%r,%r)' % (self.x, self.y)\n",
    "    \n",
    "    def __add__(self, other):\n",
    "        x = self.x + other.x\n",
    "        y = self.y + other.y\n",
    "        return Vector(x, y)\n",
    "    \n",
    "    def __sub__(self, other):\n",
    "        x = self.x - other.x\n",
    "        y = self.y - other.y\n",
    "        return Vector(x, y)\n",
    "    \n",
    "    def __mul__(self, scalar):\n",
    "        return Vector(self.x * scalar, self.y * scalar)\n",
    "    \n",
    "    def __abs__(self):\n",
    "        return hypot(self.x, self.y)\n",
    "    \n",
    "    def __bool__(self):\n",
    "        return bool(abs(self))\n",
    "    \n",
    "    def __eq__(self, other):\n",
    "        return self.x == other.x and self.y == other.y\n",
    "    \n",
    "    def __lt__(self, other):\n",
    "        return abs(self) < abs(other)\n",
    "    \n",
    "    def __gt__(self, other):\n",
    "        return abs(self) > abs(other)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 192,
   "id": "f597cb96",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "False\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "v1 = Vector(1,1)\n",
    "v2 = Vector(1,1)\n",
    "v3 = Vector(1,2)\n",
    "\n",
    "print(v1 == v2)\n",
    "print(v1 == v3)\n",
    "\n",
    "print(v3 > v1)\n",
    "print(v1 < v3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 255,
   "id": "1d29e63d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on built-in function isinstance in module builtins:\n",
      "\n",
      "isinstance(obj, class_or_tuple, /)\n",
      "    Return whether an object is an instance of a class or of a subclass thereof.\n",
      "    \n",
      "    A tuple, as in ``isinstance(x, (A, B, ...))``, may be given as the target to\n",
      "    check against. This is equivalent to ``isinstance(x, A) or isinstance(x, B)\n",
      "    or ...`` etc.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "#查看帮助\n",
    "help(isinstance)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6de82ac5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
